package com.trailwalker.mediaplayer.service;

import com.trailwalker.mediaplayer.service.entity.MediaInfo;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.media.Media;
import javafx.scene.media.MediaException;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;

import java.util.List;
import java.util.Random;

/**
 * @author Trailwalker
 * @create 2021-09-01 10:11
 * @des
 */
public class Player {

    private static final String TAG = Player.class.getSimpleName();
    private MediaPlayer mMediaPlayer;
    private Config mConfig;

    private Player(Config config) {
        mConfig = config;
        Media media = new Media(mConfig.mediaList.get(mConfig.currentIndex).getUrl());
        mConfig.currentMedia = media;
        mMediaPlayer = new MediaPlayer(media);
    }

    /**
     * 停止
     */
    public void stop() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
        }
    }

    /**
     * 停止
     */
    public void pause() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
        }
    }

    /**
     * 注销
     */
    public void dispose() {
        if (mMediaPlayer != null) {
            mMediaPlayer.dispose();
        }
    }

    /**
     * 播放
     */
    public void play() {
        if (mMediaPlayer != null) {
            mMediaPlayer.play();
        }
    }

    public void playPrev() {
        mConfig.currentIndex = (mConfig.currentIndex + mConfig.mediaList.size() - 1) % mConfig.mediaList.size();
        play(mConfig.currentIndex);
    }

    public void playNext() {
        mConfig.currentIndex = (mConfig.currentIndex + mConfig.mediaList.size() + 1) % mConfig.mediaList.size();
        play(mConfig.currentIndex);
    }

    public void changePlayMode() {
        mConfig.playMode.set(mConfig.playMode.get().next());
    }

    /**
     * 播放
     */
    public void play(int index) {
        if (index >= 0 && index < mConfig.mediaList.size()) {
            dispose();
            mConfig.currentIndex = index;
            Media media = new Media(mConfig.mediaList.get(mConfig.currentIndex).getUrl());
            mConfig.currentMedia = media;
            mMediaPlayer = new MediaPlayer(media);
            init();
            mMediaPlayer.play();
        }
    }

    public int getCurrentIndex() {
        return mConfig.currentIndex;
    }

    public Media getCurrentMedia() {
        return mConfig.currentMedia;
    }

    public MediaInfo getCurrentMediaInfo() {
        return mConfig.mediaList.get(mConfig.currentIndex);
    }

    public Duration getCurrentTime() {
        Duration currentTime = null;
        if (mMediaPlayer != null) {
            currentTime = mMediaPlayer.getCurrentTime();
        }
        return currentTime;
    }

    public void init() {
        if (mMediaPlayer != null) {
            if (mConfig.volumeProperty != null) {
                mMediaPlayer.volumeProperty().bind(mConfig.volumeProperty);
            }
            if (mConfig.balanceProperty != null) {
                mMediaPlayer.balanceProperty().bind(mConfig.balanceProperty);
            }
            mMediaPlayer.currentTimeProperty().addListener(new ChangeListener<Duration>() {
                @Override
                public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
                    if (mConfig.currentTimeProperty != null) {
                        mConfig.currentTimeProperty.set(newValue.toSeconds());
                    }
                }
            });
            mMediaPlayer.setOnReady(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onReady != null) {
                        mConfig.onReady.run();
                    }
                }
            });
            mMediaPlayer.setOnError(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onError != null) {
                        mConfig.onError.run();
                    }
                }
            });
            mMediaPlayer.setOnHalted(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onHalted != null) {
                        mConfig.onHalted.run();
                    }
                }
            });
            mMediaPlayer.setOnStalled(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onStalled != null) {
                        mConfig.onStalled.run();
                    }
                }
            });
            mMediaPlayer.setOnRepeat(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onRepeat != null) {
                        mConfig.onRepeat.run();
                    }
                }
            });
            mMediaPlayer.setOnPaused(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onPaused != null) {
                        mConfig.onPaused.run();
                    }
                }
            });
            mMediaPlayer.setOnStopped(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onStopped != null) {
                        mConfig.onStopped.run();
                    }
                }
            });
            mMediaPlayer.setOnPlaying(new Runnable() {
                @Override
                public void run() {
                    if (mConfig.onPlaying != null) {
                        mConfig.onPlaying.run();
                    }
                }
            });
            mMediaPlayer.setOnEndOfMedia(new Runnable() {
                @Override
                public void run() {
                    switch (mConfig.playMode.get()) {
                        case RANDOM:
                            int randomInt = new Random().nextInt(mConfig.mediaList.size());
                            play(randomInt);
                            break;
                        case SEQUENCE:
                            int nextIndex = mConfig.currentIndex + 1;
                            if (nextIndex < mConfig.mediaList.size()) {
                                play(nextIndex);
                            }
                            break;
                        case SINGLE_CYCLE:
                            play(mConfig.currentIndex);
                            break;
                        case LIST_CYCLE:
                            playNext();
                            break;
                        default:
                            break;
                    }
                    if (mConfig.onEndOfMedia != null) {
                        mConfig.onEndOfMedia.run();
                    }
                }
            });
            mMediaPlayer.statusProperty().addListener(new ChangeListener<MediaPlayer.Status>() {
                @Override
                public void changed(ObservableValue<? extends MediaPlayer.Status> observable, MediaPlayer.Status oldValue, MediaPlayer.Status newValue) {
                    if (mConfig.statusChangeListener != null) {
                        mConfig.statusChangeListener.changed(observable, oldValue, newValue);
                    }
                }
            });
            mMediaPlayer.errorProperty().addListener(new ChangeListener<MediaException>() {
                @Override
                public void changed(ObservableValue<? extends MediaException> observable, MediaException oldValue, MediaException newValue) {
                    if (mConfig.mediaExceptionChangeListener != null) {
                        mConfig.mediaExceptionChangeListener.changed(observable, oldValue, newValue);
                    }
                }
            });
            mMediaPlayer.currentTimeProperty().addListener(new ChangeListener<Duration>() {
                @Override
                public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
                    if (mConfig.currentTimeChangeListener != null) {
                        mConfig.currentTimeChangeListener.changed(observable, oldValue, newValue);
                    }
                }
            });
            mConfig.playMode.addListener(new ChangeListener<PlayMode>() {
                @Override
                public void changed(ObservableValue<? extends PlayMode> observable, PlayMode oldValue, PlayMode newValue) {
                    if (mConfig.playModeChangeListener != null) {
                        mConfig.playModeChangeListener.changed(observable, oldValue, newValue);
                    }
                }
            });
            if (mConfig.volume != null) {
                mMediaPlayer.setVolume(mConfig.volume);
            }
            if (mConfig.rate != null) {
                mMediaPlayer.setRate(mConfig.rate);
            }
            if (mConfig.balance != null) {
                mMediaPlayer.setBalance(mConfig.balance);
            }
            if (mConfig.cycleCount != null) {
                mMediaPlayer.setCycleCount(mConfig.cycleCount);
            }
            if (mConfig.startTime != null) {
                mMediaPlayer.setStartTime(mConfig.startTime);
            }
            if (mConfig.stopTime != null) {
                mMediaPlayer.setStopTime(mConfig.stopTime);
            }
        }
    }

    /**
     * 绑定声音大小
     *
     * @param volumeProperty
     */
    public void bindVolume(DoubleProperty volumeProperty) {
        mConfig.volumeProperty = volumeProperty;
        if (mMediaPlayer != null) {
            mMediaPlayer.volumeProperty().bind(mConfig.volumeProperty);
        }
    }

    /**
     * 绑定声道平衡
     *
     * @param balanceProperty
     */
    public void bindBalance(DoubleProperty balanceProperty) {
        mConfig.balanceProperty = balanceProperty;
        if (mMediaPlayer != null) {
            mMediaPlayer.balanceProperty().bind(mConfig.balanceProperty);
        }
    }

    /**
     * 绑定进度
     *
     * @param currentTimeProperty
     */
    public void bindCurrentTime(DoubleProperty currentTimeProperty) {
        mConfig.currentTimeProperty = currentTimeProperty;
    }

    /**
     * 解绑进度
     */
    public void unbindCurrentTime() {
        mConfig.currentTimeProperty = null;
    }

    public void setOnReady(Runnable onReady) {
        mConfig.onReady = onReady;
    }

    public void setOnError(Runnable onError) {
        mConfig.onError = onError;
    }

    public void setOnHalted(Runnable onHalted) {
        mConfig.onHalted = onHalted;
    }

    public void setOnStalled(Runnable onStalled) {
        mConfig.onStalled = onStalled;
    }

    public void setOnRepeat(Runnable onRepeat) {
        mConfig.onRepeat = onRepeat;
    }

    public void setOnPaused(Runnable onPaused) {
        mConfig.onPaused = onPaused;
    }

    public void setOnStopped(Runnable onStopped) {
        mConfig.onStopped = onStopped;
    }

    public void setOnPlaying(Runnable onPlaying) {
        mConfig.onPlaying = onPlaying;
    }

    public void setOnEndOfMedia(Runnable onEndOfMedia) {
        mConfig.onEndOfMedia = onEndOfMedia;
    }

    public void addStatusListener(ChangeListener<MediaPlayer.Status> statusChangeListener) {
        mConfig.statusChangeListener = statusChangeListener;
    }

    public void addErrorListener(ChangeListener<MediaException> mediaExceptionChangeListener) {
        mConfig.mediaExceptionChangeListener = mediaExceptionChangeListener;
    }

    public void addCurrentTimeListener(ChangeListener<Duration> currentTimeListener) {
        mConfig.currentTimeChangeListener = currentTimeListener;
    }

    public void addPlayModeListener(ChangeListener<PlayMode> playModeChangeListener) {
        mConfig.playModeChangeListener = playModeChangeListener;
    }

    /**
     * 设置音量
     *
     * @param volume
     */
    public void setVolume(double volume) {
        mConfig.volume = volume;
        if (mMediaPlayer != null) {
            mMediaPlayer.setVolume(mConfig.volume);
        }
    }

    /**
     * 设置播放模式
     *
     * @param playMode
     */
    public void setPlayMode(PlayMode playMode) {
        mConfig.playMode.set(playMode);
    }

    /**
     * 设置播放速率
     *
     * @param rate
     */
    public void setRate(int rate) {
        mConfig.rate = rate;
        if (mMediaPlayer != null) {
            mMediaPlayer.setRate(mConfig.rate);
        }
    }

    /**
     * 设置平衡
     *
     * @param balance
     */
    public void setBalance(double balance) {
        mConfig.balance = balance;
        if (mMediaPlayer != null) {
            mMediaPlayer.setBalance(mConfig.balance);
        }
    }

    /**
     * 设置循环次数
     *
     * @param cycleCount
     */
    public void setCycleCount(int cycleCount) {
        mConfig.cycleCount = cycleCount;
        if (mMediaPlayer != null) {
            mMediaPlayer.setCycleCount(mConfig.cycleCount);
        }
    }

    /**
     * 设置开始时间
     *
     * @param startTimeduration
     */
    public void setStartTime(Duration startTimeduration) {
        mConfig.startTime = startTimeduration;
        if (mMediaPlayer != null) {
            mMediaPlayer.setStartTime(mConfig.startTime);
        }
    }

    /**
     * 设置结束时间
     *
     * @param stopTimeDuration
     */
    public void setStopTime(Duration stopTimeDuration) {
        mConfig.stopTime = stopTimeDuration;
        if (mMediaPlayer != null) {
            mMediaPlayer.setStopTime(mConfig.stopTime);
        }
    }

    /**
     * 跳转播放进度
     */
    public void seek(Duration seekTime) {
        if (mMediaPlayer != null) {
            mMediaPlayer.seek(seekTime);
        }
    }

    public static class Builder {

        private final Config mConfig;

        public Builder(List<MediaInfo> mediaList) {
            mConfig = new Config();
            mConfig.mediaList = mediaList;
            mConfig.playMode.set(PlayMode.RANDOM);
        }

        /**
         * 绑定进度
         *
         * @param currentTimeProperty
         */
        public Builder bindCurrentTime(DoubleProperty currentTimeProperty) {
            mConfig.currentTimeProperty = currentTimeProperty;
            return this;
        }

        public Builder bindVolume(DoubleProperty volumeProperty) {
            mConfig.volumeProperty = volumeProperty;
            return this;
        }

        public Builder bindBalance(DoubleProperty balanceProperty) {
            mConfig.balanceProperty = balanceProperty;
            return this;
        }

        public Builder setOnReady(Runnable onReady) {
            mConfig.onReady = onReady;
            return this;
        }

        public Builder setOnError(Runnable onError) {
            mConfig.onError = onError;
            return this;
        }

        public Builder setOnHalted(Runnable onHalted) {
            mConfig.onHalted = onHalted;
            return this;
        }

        public Builder setOnStalled(Runnable onStalled) {
            mConfig.onStalled = onStalled;
            return this;
        }

        public Builder setOnRepeat(Runnable onRepeat) {
            mConfig.onRepeat = onRepeat;
            return this;
        }

        public Builder setOnPaused(Runnable onPaused) {
            mConfig.onPaused = onPaused;
            return this;
        }

        public Builder setOnStopped(Runnable onStopped) {
            mConfig.onStopped = onStopped;
            return this;
        }

        public Builder setOnPlaying(Runnable onPlaying) {
            mConfig.onPlaying = onPlaying;
            return this;
        }

        public Builder setOnEndOfMedia(Runnable onEndOfMedia) {
            mConfig.onEndOfMedia = onEndOfMedia;
            return this;
        }

        public Builder addStatusListener(ChangeListener<MediaPlayer.Status> statusChangeListener) {
            mConfig.statusChangeListener = statusChangeListener;
            return this;
        }

        public Builder addCurrentTimeListener(ChangeListener<Duration> currentTimeListener) {
            mConfig.currentTimeChangeListener = currentTimeListener;
            return this;
        }

        public Builder addErrorListener(ChangeListener<MediaException> mediaExceptionChangeListener) {
            mConfig.mediaExceptionChangeListener = mediaExceptionChangeListener;
            return this;
        }

        /**
         * 设置音量
         *
         * @param volume
         */
        public Builder setVolume(double volume) {
            mConfig.volume = volume;
            return this;
        }

        /**
         * 设置播放模式
         *
         * @param playMode
         * @return
         */
        public Builder setPlayMode(PlayMode playMode) {
            mConfig.playMode.set(playMode);
            return this;
        }

        /**
         * 设置播放速率
         *
         * @param rate
         */
        public Builder setRate(int rate) {
            mConfig.rate = rate;
            return this;
        }

        /**
         * 设置平衡
         *
         * @param balance
         */
        public Builder setBalance(double balance) {
            mConfig.balance = balance;
            return this;
        }

        /**
         * 设置循环次数
         *
         * @param cycleCount
         */
        public Builder setCycleCount(int cycleCount) {
            mConfig.cycleCount = cycleCount;
            return this;
        }

        /**
         * 设置开始时间
         *
         * @param startTimeduration
         */
        public Builder setStartTime(Duration startTimeduration) {
            mConfig.startTime = startTimeduration;
            return this;
        }

        /**
         * 设置结束时间
         *
         * @param stopTimeDuration
         */
        public Builder setStopTime(Duration stopTimeDuration) {
            mConfig.stopTime = stopTimeDuration;
            return this;
        }

        public Player build() {
            Player player = new Player(mConfig);
            player.init();
            return player;
        }
    }

    public static class Config {
        public Media currentMedia;
        private SimpleObjectProperty<PlayMode> playMode = new SimpleObjectProperty<>();
        public List<MediaInfo> mediaList;
        public int currentIndex = 0;
        public Duration startTime;
        public Duration stopTime;
        public Integer cycleCount;
        public Double balance;
        public DoubleProperty balanceProperty;
        public Integer rate;
        public Double volume;
        public DoubleProperty volumeProperty;
        public DoubleProperty currentTimeProperty;
        public ChangeListener<MediaException> mediaExceptionChangeListener;
        public ChangeListener<MediaPlayer.Status> statusChangeListener;
        public ChangeListener<Duration> currentTimeChangeListener;
        public ChangeListener<PlayMode> playModeChangeListener;
        /**
         * OnEndOfMedia 播放结束
         */
        public Runnable onEndOfMedia;
        /**
         * OnPlaying 正在播放
         */
        public Runnable onPlaying;
        /**
         * OnStopped 停止重置
         */
        public Runnable onStopped;
        /**
         * OnPaused 暂停
         */
        public Runnable onPaused;
        /**
         * OnRepeat 重复播放
         */
        public Runnable onRepeat;
        /**
         * OnStalled 进入缓冲
         */
        public Runnable onStalled;
        /**
         * OnHalted 出现了无法恢复的错误
         */
        public Runnable onHalted;
        /**
         * OnError 播放出错
         */
        public Runnable onError;
        /**
         * OnReady 准备完成
         */
        public Runnable onReady;
    }

    public enum PlayMode {
        /**
         * 随机播放
         */
        RANDOM(0, "随机播放"),
        /**
         * 顺序播放
         */
        SEQUENCE(1, "顺序播放"),
        /**
         * 单曲循环
         */
        SINGLE_CYCLE(2, "单曲循环"),
        /**
         * 列表循环
         */
        LIST_CYCLE(3, "列表循环");

        private int code;
        private String message;

        private static PlayMode[] vals = values();

        PlayMode(int code, String message) {
            this.code = code;
            this.message = message;
        }

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        /**
         * 是否有上一个
         *
         * @return
         */
        public boolean hasPrevious() {
            if (this.ordinal() <= 0) {
                return false;
            }
            return true;
        }

        /**
         * 是否有下一个
         *
         * @return
         */
        public boolean hasNext() {
            if (this.ordinal() >= vals.length - 1) {
                return false;
            }
            return true;
        }

        /**
         * 上一个
         *
         * @return
         */
        public PlayMode previous() {
            return vals[(this.ordinal() + vals.length - 1) % vals.length];
        }

        /**
         * 下一个
         *
         * @return
         */
        public PlayMode next() {
            return vals[(this.ordinal() + vals.length + 1) % vals.length];
        }
    }
}
